<?php
/**************************************************************************************************
	$Id: admin.php,v 1.13 2002/07/27 01:46:34 bboy Exp $
	Functional and useful example of a web-based DNS administration interface.
**************************************************************************************************/

$dbhost = "localhost";
$dbuser = "username";
$dbpass = "password";
$dbname = "mydns";

$ttl_min = 300;
$refresh_min = 300;
$retry_min = 300;
$expire_min = 86400;

/* If the result group is larger than this number of records, results will be output on
	separate pages. */
$result_group_size = 20;


/* A few convenience functions */
function esc($str) { return mysql_escape_string($str); }
function nf($n) { return number_format($n); }
function S($n) { return ($n == 1 ? "" : "s"); }


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	OUTPUT_HELP
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function output_help()
{
	global $result_group_size;

?>
<CENTER><DIV class=help>
Enter a search query in the input box located at the upper left corner of this page to edit
DNS records.
<P>
Acceptable forms of input include:
<P>
<UL>
<LI>
	<B>A fully qualified domain name (FQDN)</B><BR>
	<I>example:</I>&nbsp; &quot;<TT>webserver.example.com</TT>&quot;<BR>
	<BR>
	Display and edit all DNS data exactly matching the FQDN specified.
	<P>

<LI>
	<B>A zone name</B><BR>
	<I>example:</I>&nbsp; &quot;<TT>example.com</TT>&quot;<BR>
	<BR>
	Display and edit all DNS data for the specified zone, as well as the SOA record for that
	zone.  If there are more than <?php echo number_format($result_group_size); ?> resource
	records for the zone, multiple pages will be displayed.<BR>
	<P>

<LI>
	<B>An IP address</B><BR>
	<I>example:</I>&nbsp; &quot;<TT>192.168.1.1</TT>&quot;<BR>
	<BR>
	Display and edit the PTR record for the specified IP address.
	<P>

<LI>
	<B>A partial IP address</B><BR>
	<I>example:</I>&nbsp; &quot;<TT>192.168.1</TT>&quot;<BR>
	<BR>
	Display and edit the PTR records for all addresses within that class C.
	<P>
</UL>

</DIV></CENTER>
<?php
}
/*--- output_help() -----------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	OPEN_PAGE
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function open_page($title = "MyDNS")
{
	global $PHP_SELF, $query, $action;
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML lang="en">
<HEAD>
	<META http-equiv="Content-Type" content="text/html; charset=US-ASCII">
	<TITLE><?php echo $title; ?><?php if (strlen($query)) echo ": $query"; ?></TITLE>
	<STYLE type="text/css">
	<!--
		A { color: blue; text-decoration: none; }
		A:hover { color: red; text-decoration: underline; }

		BODY {
			background: #FFFFEE;
			color: #663300;
			margin: 1% 10% 3% 10%;
		}

		TABLE {
			background: #FFFFCC;
			padding: 10px;
			font-family: monospace;
		}

		DIV.help {
			background: #FFFFCC;
			padding: 1em;
			font-family: sans-serif;
			width: 75%;
			margin-top: 3em;
			border: #663300 solid 1px;
			text-align: left;
		}

		TABLE.header {
			width: 100%;
			background: #FFFFEE;
			border-bottom: #663300 solid 2px;
			padding-bottom: 2px;
			font-family: sans-serif;
			font-size: 75%;
			vertical-align: middle;
		}

		TABLE.rr {
			width: 100%;
		}

		TABLE.notice {
			padding: 10px;
			font-family: sans-serif;
			font-weight: bold;
			text-align: center;
		}

		H1 {
			font-family: sans-serif;
			font-weight: bold;
			font-size: 300%;
		}

		CAPTION {
			background: #FFFFCC;
			padding: 6px;
			border-bottom: #663300 solid 1px;
			font-family: sans-serif;
			font-size: 150%;
			font-weight: bold;
			text-align: left;
		}

		TD {
			padding: 3px 5px 3px 5px;
		}

		TD.leftindent {
			padding: 3px 10px 3px 100px;
		}

		TD.rrinsert {
			padding-top: 20px;
		}

		TD.offsetbox {
			padding-bottom: 20px;
		}

		TABLE.offset {
			font-size: 70%;
			color: #999999;
			border-bottom: #663300 solid 1px;
			padding: 0px;
		}

		TD.centeroffset {
			color: #663300;
		}

		TT {
			font-weight: bold;
		}
	-->
	</STYLE>
</HEAD>
<BODY>
<!-- Page header -->
<FORM action="<?php echo $PHP_SELF; ?>" method=GET>
<TABLE class=header>
	<TR>
		<TD align=left>
			<INPUT type=TEXT name=query value="<?php echo $query; ?>" size=25 maxlength=255
			 title="input zone or FQDN to edit records">
		</TD>
		<TD align=right>
			<IMG src="graphics/mydns-logo.png" alt="MyDNS">
		</TD>
	</TR>
</TABLE>
</FORM>
<?php
}
/*--- open_page() -------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	CLOSE_PAGE
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function close_page()
{
?>
</BODY>
</HTML>
<?php
}
/*--- close_page() ------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	OPEN_TABLE
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function open_table($class = NULL, $caption = NULL)
{
	echo "<P>\n";
	echo "<TABLE align=center" . ($class ? " class=$class" : "") . ">\n";
	if ($caption)
		echo "<CAPTION>$caption</CAPTION>";
}
/*--- open_table() ------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	CLOSE_TABLE
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function close_table()
{
	echo "</TABLE>\n";
	echo "</P>\n";
}
/*--- close_table() -----------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	NOTICE
	Output an important message in a conspicuous box.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function notice($msg)
{
?>
<P>
	<TABLE class=notice align=center>
		<TR>
			<TD><?php echo $msg; ?></TD>
		</TR>
	</TABLE>
</P>
<?php
	return (-1);
}
/*--- notice() ----------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	FATAL
	Output fatal error and exit.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function fatal($msg)
{
	notice($msg);
	close_page();
	exit;
}
function fatalSQL($msg) { fatal($msg . "<BR><DIV class=main>" . mysql_error() . "</DIV>"); }
/*--- fatal() -----------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	SQLCOUNT
	Simple wrapper to return a numeric value from a query whose result contains only one row and
	column.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function sqlcount($query)
{
	$res = mysql_query($query) or fatalSQL("Error executing SQL count.");
	$row = mysql_fetch_row($res);
	return ($row[0]);
}
/*--- sqlcount() --------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	FIXFQDN
	Trims the specified name, makes sure it has a dot at the end.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function fixfqdn($name)
{
	$name = trim($name);
	$len = strlen($name);
	if ($len && $name[($len-1)] != '.')
		$name .= ".";
	return ($name);
}
/*--- fixfqdn() ---------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	DURATION
	Output the human-friendly duration of time represented by a specified number of seconds.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function duration($seconds)
{
	$weeks = floor($seconds / 604800); $seconds -= ($weeks * 604800);
	$days = floor($seconds / 86400); $seconds -= ($days * 86400);
	$hours = floor($seconds / 3600); $seconds -= ($hours * 3600);
	$minutes = floor($seconds / 60); $seconds -= ($minutes * 60);
	if ($weeks) $rv .= "$weeks" . " week" . S($weeks);
	if ($days) $rv .= (strlen($rv) ? ", " : "") . "$days" . " day" . S($days);
	if ($hours) $rv .= (strlen($rv) ? ", " : "") . "$hours" . " hour" . S($hours);
	if ($minutes) $rv .= (strlen($rv) ? ", " : "") . "$minutes" . " minute" . S($minutes);
	if ($seconds || !strlen($rv))
		$rv .= (strlen($rv) ? ", " : "") . "$seconds" . "second" . S($seconds);
	return ($rv);
}
/*--- duration() --------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	SERIAL_GUESS
	Attempt to guess a good value for incrementing serial number.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function serial_guess($serial)
{
	$now = time();
	$low = $now - (31449600 * 2);							/* Time 2 years ago */
	$high = $now + (31449600 * 1);						/* Time 1 year hence */
	$curyear = (int)strftime("%Y", $now);				/* The current year */
	$timestamp = strftime("%Y%m%d", $now);				/* The current timestamp */

	/* If the first four characters look like a year, suggest a YYYYMMDD type format */
	$first = substr($serial, 0, 4);
	if ($first >= ($curyear-10) && $first <= ($curyear+1))
	{
		$rv = substr($timestamp, 0, strlen($serial));
		return ("current " . substr("YYYYMMDD", 0, strlen($serial)) . " is $rv");
	}

	/* If it looks like a Unix timestamp, return the current timestamp */
	if ($serial >= $low && $serial <= $high)
	{
		if ($now == $serial) $now++;
		return ("current Unix timestamp is $now");
	}

	/* If the current serial is a number that's less than the unix time 10 years ago, assume
		the serial is a simple number being incremented */
	if ($serial < $low)
		return ("suggested update value: " . ($serial+1));

	/* Otherwise return the "timestamp of choice" - YYYYMMDD */
	return ("current YYYYMMDD is $timestamp");
}
/*--- serial_guess() ----------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	LOAD_SOA
	Attempts to load a SOA record for the specified query.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function load_soa($origin)
{
	global $host;		/* Any extraneous hostname found prior to the origin */

	$origin = fixfqdn($origin);
	for ($n = 0; $n < strlen($origin); $n++)
		if ($n == 0 || $origin[$n] == '.')
		{
			$part = substr($origin, ($n == 0 ? $n : ($n + 1)));
			if (strlen($part))
			{
				$res = mysql_query("SELECT * FROM soa WHERE origin='".mysql_escape_string($part)."'")
					or fatalSQL("Error locating SOA for &quot;$part&quot;.");
				if ($soa = mysql_fetch_array($res))
				{
					if (strlen($part) != strlen($origin))
					{
						$host = substr($origin, 0, strlen($origin) - strlen($part));
						if ($host[strlen($host)-1] == '.')
							$host = substr($host, 0, strlen($host)-1);
					}
					return ($soa);
				}
			}
		}
	return 0;
}
/*--- load_soa() --------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	SOA_FORM
	Outputs the "edit soa" form.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function soa_form($soa)
{
	global $PHP_SELF, $query;

?>
<FORM method=POST action="<?php echo $PHP_SELF; ?>">
<INPUT type=hidden name=action value="soaupdate">
<INPUT type=hidden name=query value="<?php echo $query; ?>">
<INPUT type=hidden name=soa value="<?php echo $soa[id]; ?>">
<TABLE align=center class=soa>
	<TR>
		<TD colspan=3>
			<TABLE><TR>
				<TD><SPAN title="zone id <?php echo $soa[id]; ?>"><B><?php echo $soa[origin]; ?></B></SPAN></TD>
				<TD>
					<INPUT type=text name=ttl value="<?php echo $soa[ttl]; ?>" size=7
					 title="The TTL for this SOA record.">
				</TD>
				<TD>IN SOA</TD>
				<TD>
					<INPUT type=text name=ns value="<?php echo $soa[ns]; ?>" size=30 maxlength=255
					 title="The authoritative nameserver for this zone.">
				</TD>
				<TD>
					<INPUT type=text name=mbox value="<?php echo $soa[mbox]; ?>" size=30 maxlength=255
					 title="The administrative mailbox for this zone."> (
				</TD>
			</TR></TABLE>
		</TD>
	</TR>
	<TR>
		<TD class=leftindent>
			<INPUT type=text name=serial value="<?php echo $soa[serial]; ?>" size=15
			 title="The serial number for this zone.">
		</TD>
		<TD colspan=2>; Serial (<?php echo serial_guess($soa[serial]); ?>)</TD>
	</TR>
	<TR>
		<TD class=leftindent>
			<INPUT type=text name=refresh value="<?php echo $soa[refresh]; ?>" size=7
			 title="SOA refresh time, in seconds.">
		</TD>
		<TD colspan=2>; Refresh (currently <?php echo duration($soa[refresh]); ?>)</TD>
	</TR>
	<TR>
		<TD class=leftindent>
			<INPUT type=text name=retry value="<?php echo $soa[retry]; ?>" size=7
			 title="SOA retry time, in seconds.">
		</TD>
		<TD colspan=2>; Retry (currently <?php echo duration($soa[retry]); ?>)</TD>
	</TR>
	<TR>
		<TD class=leftindent>
			<INPUT type=text name=expire value="<?php echo $soa[expire]; ?>" size=7
			 title="SOA expire time, in seconds.">
		</TD>
		<TD colspan=2>; Expire (currently <?php echo duration($soa[expire]); ?>)</TD>
	</TR>
	<TR>
		<TD class=leftindent>
			<INPUT type=text name=minimum value="<?php echo $soa[minimum]; ?>" size=7
			 title="Minimum TTL for records within zone, in seconds.">
			)
		</TD>
		<TD>; Minimum TTL for zone (currently <?php echo duration($soa[minimum]); ?>)</TD>
		<TD align=right><INPUT type=submit value="Update"></TD>
	</TR>
</TABLE>
</FORM>
<?php
}
/*--- soa_form() --------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	RRFORM
	Outputs the one-row "form" for a resource record.
	If $rr is not-NULL, output a form with Update and Delete buttons.
	If $rr is NULL, output a form with empty fields and an "add" button.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function rrform($soa, $rr = NULL, $force_name = NULL)
{
	global $PHP_SELF, $query;

	$action = ($rr ? "rrupdate" : "rrinsert");

?>
<FORM method=POST action="<?php echo $PHP_SELF; ?>">
<INPUT type=hidden name=query value="<?php echo $query; ?>">
<INPUT type=hidden name=action value="<?php echo $action; ?>">
<INPUT type=hidden name=soa value="<?php echo $soa[id]; ?>">
<INPUT type=hidden name=rr value="<?php echo $rr[id]; ?>">
	<TR>
		<TD class="<?php echo $action; ?>">
<?php
	if ($rr)
	{
		echo "<SPAN title=\"rr $rr[id] \nzone $rr[zone]\">";
		echo (strlen($rr[name]) ? $rr[name] : "<B>$soa[origin]</B>");
		echo "</SPAN>";
		echo "<INPUT type=hidden name=name value=\"$rr[name]\">";
	}
	else if ($force_name)
	{
		echo $force_name;
		echo "<INPUT type=hidden name=name value=\"$rr[name]\">";
	}
	else
		echo "<INPUT type=text name=name size=30 title=\"The name for this resource record.\">";
?>
		</TD>
		<TD class="<?php echo $action; ?>">
			<INPUT type=text name=ttl value="<?php echo ($rr ? $rr[ttl] : $soa[minimum]); ?>" size=7
			 title="The TTL for this resource record.">
		</TD>
		<TD class="<?php echo $action; ?>">IN</TD>
		<TD class="<?php echo $action; ?>">
<?php
	echo "<SELECT name=type size=1>";
	foreach (array("A","AAAA","CNAME","MX","NS","TXT") as $type)
		echo "<OPTION" . (!strcasecmp($rr[type], $type) ? " selected" : "") . ">$type";
	echo "</SELECT>";
?>
		</TD>
		<TD class="<?php echo $action; ?>">
			<INPUT type=text name=aux value="<?php echo ($rr[aux] ? $rr[aux] : ""); ?>" size=3
			 title="Auxillary value for this resource record.">
		</TD>
		<TD class="<?php echo $action; ?>">
			<INPUT type=text name=data value="<?php echo $rr[data]; ?>" size=30 maxlength=255
			 title="Data for this resource record.">
		</TD>
<?php
	if ($rr)				/* Output "Update" and "Delete" buttons */
	{
?>
		<TD class="<?php echo $action; ?>"><INPUT type=submit name="rraction" value="Update"></TD>
		<TD class="<?php echo $action; ?>"><INPUT type=submit name="rraction" value="Delete"></TD>
<?php
	}
	else					/* $rr is NULL; output a "Create" button */
	{
?>
		<TD class="<?php echo $action; ?>" colspan=2><INPUT type=submit value="Create new RR"></TD>
<?php
	}
?>
	</TR>
</FORM>
<?php
}
/*--- rrform() ----------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	PTRFORM
	Outputs the one-row "form" for a PTR record.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function ptrform($ptr = NULL)
{
	global $PHP_SELF, $query;

	$action = ($ptr ? "ptrupdate" : "ptrinsert");

?>
<FORM method=POST action="<?php echo $PHP_SELF; ?>">
<INPUT type=hidden name=query value="<?php echo $query; ?>">
<INPUT type=hidden name=action value="<?php echo $action; ?>">
<INPUT type=hidden name=ptr value="<?php echo $ptr[id]; ?>">
	<TR>
		<TD class="<?php echo $action; ?>">
<?php
	if ($ptr)
	{
		echo "<SPAN title=\"ptr $ptr[id]\">$ptr[dd]</SPAN>";
		echo "<INPUT type=hidden name=dd value=\"$ptr[dd]\">";
	}
	else
		echo "<INPUT type=text name=dd size=16 value=\"$query.\" title=\"The IP address.\">";
?>
		</TD>
		<TD class="<?php echo $action; ?>">IN PTR</TD>
		<TD class="<?php echo $action; ?>">
			<INPUT type=text name=name value="<?php echo $ptr[name]; ?>" size=30 maxlength=255
			 title="Name associated with this address.">
		</TD>
<?php
	if ($ptr)				/* Output "Update" and "Delete" buttons */
	{
?>
		<TD class="<?php echo $action; ?>"><INPUT type=submit name="ptraction" value="Update"></TD>
		<TD class="<?php echo $action; ?>"><INPUT type=submit name="ptraction" value="Delete"></TD>
<?php
	}
	else					/* $ptr is NULL; output a "Create" button */
	{
?>
		<TD class="<?php echo $action; ?>" colspan=2><INPUT type=submit value="Create new PTR"></TD>
<?php
	}
?>
	</TR>
</FORM>
<?php
}
/*--- ptrform() ---------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	OUTPUT_OFFSET_SELECT
	For large result sets, outputs a menu for navigating within the result.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function output_offset_select($offset, $total)
{
	global $result_group_size, $query;

	if ($total <= $result_group_size)
		return;

	/* Construct LEFT link buttons */
	for ($ct = 0, $cur = $offset - $result_group_size; $ct < 4; $ct++)
	{
		$txt = str_repeat("&lt;", ($ct + 1));
		if ($cur >= 0)
			$l[$ct] = "<A href=\"?query=$query&offset=$cur\" title=\"Back ".($offset-$cur)." records\">$txt</A>";
		else
			$l[$ct] = "$txt";
		$cur -= ($result_group_size * ($ct+1) * 2);
	}
	/* Construct RIGHT link buttons */
	for ($ct = 0, $cur = $offset + $result_group_size; $ct < 4; $ct++)
	{
		$txt = str_repeat("&gt;", ($ct + 1));
		if ($cur <= $total)
			$r[$ct] = "<A href=\"?query=$query&offset=$cur\" title=\"Forward ".($cur-$offset)." records\">$txt</A>";
		else
			$r[$ct] = "$txt";
		$cur += ($result_group_size * ($ct+1) * 2);
	}


?>
<TR>
	<TD colspan=8 class=offsetbox>
		<TABLE class=offset width="100%">
			<TR>
				<TD align=left>
<?php
	echo $l[0] . " &nbsp; ";
	echo $l[1] . " &nbsp; ";
	echo $l[2] . " &nbsp; ";
	echo $l[3] . " &nbsp; ";
?>
				</TD>
				<TD align=center class=centeroffset>
<?php
	if (($top = $offset + $result_group_size) > $total)
		$top = $total;
	echo nf($offset+1)."-".nf($top)." of ".nf($total);
?>
				</TD>
				<TD align=right>
<?php
	echo $r[3] . " &nbsp; ";
	echo $r[2] . " &nbsp; ";
	echo $r[1] . " &nbsp; ";
	echo $r[0] . " &nbsp; ";
?>
				</TD>
			</TR>
		</TABLE>
	</TD>
</TR>
<?php
}
/*--- output_offset_select() --------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	PTR_EDIT
	Assuming the query is in CIDR notation, edit PTR records.
	CIDR to range conversion by philippe-at-cyberabuse.org.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function ptr_edit($query)
{
	global $offset, $result_group_size;

	$offset = (int)$offset;

	/* If partial IP specified, replacing missing segments and get range */
	$quad = explode(".", $query);
	if (!$quad[1]) $quad[1] = "x";
	if (!$quad[2]) $quad[2] = "x";
	if (!$quad[3]) $quad[3] = "x";
	$mask = "$quad[0].$quad[1].$quad[2].$quad[3]";
	$low = str_replace("x", "0", $mask);
	$high = str_replace("x", "255", $mask);

	$where = "WHERE ip >= INET_ATON('$low') AND ip <= INET_ATON('$high') ORDER BY ip";

	$total = sqlcount("SELECT COUNT(*) FROM ptr $where");

	$res = mysql_query("SELECT id,INET_NTOA(ip) AS dd,name,ttl FROM ptr $where ".
							 "LIMIT $offset,$result_group_size")
		or fatalSQL("Error loading PTR records");

	open_table("ptr");
	output_offset_select($offset, $total);
	while ($ptr = mysql_fetch_array($res))
		ptrform($ptr);
	if ($total > 1)
		ptrform();
	close_table();
}
/*--- ptr_edit() --------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	TOP_LEVEL_RR_EDIT_FORM
	Outputs the form containing primary zone data (i.e. rr's with blank names or `*').
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function top_level_rr_edit_form($soa)
{
	open_table("rr");
	$q = "SELECT * FROM rr WHERE zone=$soa[id] AND name='' ORDER BY name,type";
	$res = mysql_query($q)
		or fatalSQL("Error loading primary resource records for &quot;$soa[origin]&quot; zone.");
	while ($rr = mysql_fetch_array($res))
		rrform($soa, $rr);
	rrform($soa);
	close_table();
}
/*--- top_level_rr_edit_form() ------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	ZONE_RR_EDIT_FORM
	Outputs a list of resource records (but not top level rescords) to edit.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function zone_rr_edit_form($soa, $host)
{
	global $offset, $result_group_size;

	$offset = (int)$offset;

	if (!strlen($host))
		$where = "WHERE zone=$soa[id] AND name != '' ORDER BY name,type";
	else
		$where = "WHERE zone=$soa[id] AND name = '".esc($host)."' ORDER BY name,type";

	$total = sqlcount("SELECT COUNT(*) FROM rr $where");

	$res = mysql_query("SELECT * FROM rr $where LIMIT $offset,$result_group_size")
		or fatalSQL("Error loading resource records for &quot;$host.$soa[origin]&quot;");

	if (!mysql_num_rows($res))
	{
		notice("No records matching host &quot;$host&quot; in zone &quot;$soa[origin]&quot;");
		return;
	}

	open_table("rr", "Resource records");
	output_offset_select($offset, $total);
	while ($rr = mysql_fetch_array($res))
		rrform($soa, $rr);
	rrform($soa);
	close_table();
}
/*--- zone_rr_edit_form() -----------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	SHOW_FORM
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function show_form($query)
{
	global $host;

	if (!strlen($query))
	{
		output_help();
		return;
	}

	/* Edit PTR records if the query looks like numbers-and-dots */
	if (strlen($query) && strspn($query, "0123456789.") == strlen($query))
	{
		ptr_edit($query);
		close_page();
	}
	else
	{
		$soa = load_soa($query) or fatal("Unable to find any records matching your query.");
		if (!strlen($host))
		{
			soa_form($soa);
			top_level_rr_edit_form($soa);
		}
		zone_rr_edit_form($soa, $host);
	}
}
/*--- show_form() -------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	CHECK_SOA_FORMDATA
	Returns 0 if everything is OK, -1 if not.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function check_soa_formdata()
{
	global $HTTP_POST_VARS;
	global $ttl_min, $refresh_min, $retry_min, $expire_min;

	/* Load SOA record for record being updated */
	$res = mysql_query("SELECT * FROM soa WHERE id=".(int)($HTTP_POST_VARS[soa]))
		or fatalSQL("Error loading soa {$HTTP_POST_VARS[soa]}.");
	$soa = mysql_fetch_array($res) or fatal("Unable to find soa {$HTTP_POST_VARS[soa]}.");
	$soa[minimum] = (int)$HTTP_POST_VARS[minimum];

	$fail = "<BR>SOA record not updated.";

	foreach ($HTTP_POST_VARS as $n => $v)
		$HTTP_POST_VARS[$n] = trim($v);

	foreach ($HTTP_POST_VARS as $n => $v)
	switch ($n)
	{
		case "ns":
			$v = $HTTP_POST_VARS[$n] = fixfqdn($v);
			if (!strlen($v)) return notice("Authoritative nameserver field empty.".$fail);
			if (strlen($v) > 255) return notice("Authoritative nameserver field too long.".$fail);
			break;

		case "mbox":
			$v = $HTTP_POST_VARS[$n] = fixfqdn($v);
			if (!strlen($v)) return notice("Administrative mailbox field empty.".$fail);
			if (strlen($v) > 255) return notice("Administrative mailbox field too long.".$fail);
			break;

		case "ttl":
			if ($v < $ttl_min)
				return notice("TTL unreasonably short.<BR>".
						 "The minimum allowable value is $ttl_min (".duration($ttl_min).").".$fail);
			if ($v < $soa[minimum])
			{
				$HTTP_POST_VARS[ttl] = $soa[minimum];
				notice("TTL raised to {$soa[minimum]} (".duration($soa[minimum]).") ".
						 "to comply with the minimum TTL for this zone.");
			}
			break;

		case "serial":
			if (!strlen($v))
				return notice("Serial field empty.".$fail);
			break;

		case "refresh":
			if ($v < $refresh_min)
				return notice("Refresh time unreasonably short.<BR>".
								  "The minimum allowable value is $refresh_min ".
								  "(".duration($refresh_min).").".$fail);
			break;

		case "retry":
			if ($v < $retry_min)
				return notice("Retry time unreasonably short.<BR>".
								  "The minimum allowable value is $retry_min ".
								  "(".duration($retry_min).").".$fail);
			break;

		case "expire":
			if ($v < $expire_min)
				return notice("Expiration time unreasonably short.<BR>".
								  "The minimum allowable value is $expire_min ".
								  "(".duration($expire_min).").".$fail);
			break;

		case "minimum":
			if ($v < $minimum_min)
				return notice("Minimum TTL unreasonably short.<BR>".
								  "The minimum allowable value is $ttl_min ".
								  "(".duration($ttl_min).").".$fail);
			break;
	}
	return (0);
}
/*--- check_soa_formdata() ----------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	SOA_UPDATE
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function soaupdate()
{
	global $HTTP_POST_VARS;

	if (check_soa_formdata() == -1)
		return;

	$q  = "UPDATE soa SET ";
	$q .= "ns='" . esc($HTTP_POST_VARS["ns"]) . "'";
	$q .= ",mbox='" . esc($HTTP_POST_VARS["mbox"]) . "'";
	$q .= ",serial=" . (int)($HTTP_POST_VARS["serial"]);
	$q .= ",refresh=" . (int)($HTTP_POST_VARS["refresh"]);
	$q .= ",retry=" . (int)($HTTP_POST_VARS["retry"]);
	$q .= ",expire=" . (int)($HTTP_POST_VARS["expire"]);
	$q .= ",minimum=" . (int)($HTTP_POST_VARS["minimum"]);
	$q .= ",ttl=" . (int)($HTTP_POST_VARS["ttl"]);
	$q .= " WHERE id=" . (int)($HTTP_POST_VARS["soa"]);

	mysql_query($q) or fatalSQL("Error updating SOA.");
}
/*--- soaupdate() ------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	RR_DELETE
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function rr_delete()
{
	global $HTTP_POST_VARS;

	$q  = "DELETE FROM rr WHERE id=" . (int)$HTTP_POST_VARS[rr];

	mysql_query($q) or fatalSQL("Error deleting resource record.");
}
/*--- rr_delete() -------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	CHECK_RR_FORMDATA
	Returns 0 if everything is OK, -1 if not.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function check_rr_formdata($desc = "updated")
{
	global $HTTP_POST_VARS;
	global $ttl_min;

	/* Load SOA record for record being updated */
	$res = mysql_query("SELECT * FROM soa WHERE id=".(int)($HTTP_POST_VARS[soa]))
		or fatalSQL("Error loading soa {$HTTP_POST_VARS[soa]}.");
	$soa = mysql_fetch_array($res) or fatal("Unable to find soa {$HTTP_POST_VARS[soa]}.");

	$fail = "<BR>Resource record not $desc.";

	foreach ($HTTP_POST_VARS as $n => $v)
		$HTTP_POST_VARS[$n] = trim($v);

	foreach ($HTTP_POST_VARS as $n => $v)
	switch ($n)
	{
		case "name":
			if ($v == $soa[origin] || "$v." == $soa[origin])
				$v = $HTTP_POST_VARS[name] = "";
			while ($v[strlen($v)-1] == '.')
				$v = $HTTP_POST_VARS[name] = substr($v, 0, strlen($v)-1);
			if (strlen($v) > 64) return notice("Name field too long.".$fail);
			break;

		case "ttl":
			if ($v < $soa[minimum])
			{
				$v = $HTTP_POST_VARS[ttl] = $soa[minimum];
				notice("TTL raised to {$soa[minimum]} (".duration($soa[minimum]).") ".
						 "to comply with the minimum TTL for this zone.");
			}
			if ($v < $ttl_min)
				return notice("TTL unreasonably short.<BR>".
						 "The minimum allowable value is $ttl_min (".duration($ttl_min).").".$fail);
			break;

		case "type":		/* Do special processing on `data' for certain types */
			switch ($v)
			{
				case "A":
					/* XXX: Validate IPv4 address */
					break;

				case "AAAA":
					/* XXX: Validate IPv6 address */
					break;

				case "CNAME":
					$HTTP_POST_VARS[data] = fixfqdn($HTTP_POST_VARS[data]);
					$HTTP_POST_VARS[aux] = 0;
					break;

				case "MX":
					$HTTP_POST_VARS[data] = fixfqdn($HTTP_POST_VARS[data]);
					if ($HTTP_POST_VARS[aux] <= 0)
						$HTTP_POST_VARS[aux] = 1;
					break;

				case "NS":
					$HTTP_POST_VARS[data] = fixfqdn($HTTP_POST_VARS[data]);
					$HTTP_POST_VARS[aux] = 0;
					break;

				default:		/* "aux" is only used with MX records */
					$HTTP_POST_VARS[aux] = 0;
					break;
			}
			break;

		case "data":
			if (!strlen($v)) return notice("Data field empty.".$fail);
			if (strlen($v) > 255) return notice("Data field too long.".$fail);
			break;
	}
	return (0);
}
/*--- check_rr_formdata() -----------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	RRUPDATE
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function rrupdate()
{
	global $HTTP_POST_VARS;

	if (check_rr_formdata("updated") == -1)
		return;

	$q  = "UPDATE rr SET ";
	$q .= "name='" . esc($HTTP_POST_VARS["name"]) . "'";
	$q .= ",type='" . esc($HTTP_POST_VARS["type"]) . "'";
	$q .= ",data='" . esc($HTTP_POST_VARS["data"]) . "'";
	$q .= ",aux=" . (int)($HTTP_POST_VARS["aux"]);
	$q .= ",ttl=" . (int)($HTTP_POST_VARS["ttl"]);
	$q .= " WHERE id=" . (int)($HTTP_POST_VARS["rr"]);

	mysql_query($q) or fatalSQL("Error updating resource record.");
}
/*--- rrupdate() --------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	RRINSERT
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function rrinsert()
{
	global $HTTP_POST_VARS;

	if (check_rr_formdata("created") == -1)
		return;

	$q  = "INSERT INTO rr SET ";
	$q .= "zone=" . (int)($HTTP_POST_VARS["soa"]);
	$q .= ",name='" . esc($HTTP_POST_VARS["name"]) . "'";
	$q .= ",type='" . esc($HTTP_POST_VARS["type"]) . "'";
	$q .= ",data='" . esc($HTTP_POST_VARS["data"]) . "'";
	$q .= ",aux=" . (int)($HTTP_POST_VARS["aux"]);
	$q .= ",ttl=" . (int)($HTTP_POST_VARS["ttl"]);

	mysql_query($q) or fatalSQL("Error creating resource record.");
}
/*--- rrinsert() --------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	CHECK_PTR_FORMDATA
	Returns 0 if everything is OK, -1 if not.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function check_ptr_formdata($desc = "updated")
{
	global $HTTP_POST_VARS;

	foreach ($HTTP_POST_VARS as $n => $v)
		$HTTP_POST_VARS[$n] = trim($v);

	$fail = "<BR>PTR record not $desc.";

	$dd = $HTTP_POST_VARS[dd];
	$quad = explode(".", $dd);
	if ((int)$quad[0] < 0 || (int)$quad[0] > 255) return notice("IP address out of range.".$fail);
	if ((int)$quad[1] < 0 || (int)$quad[1] > 255) return notice("IP address out of range.".$fail);
	if ((int)$quad[2] < 0 || (int)$quad[2] > 255) return notice("IP address out of range.".$fail);
	if ((int)$quad[3] < 0 || (int)$quad[3] > 255) return notice("IP address out of range.".$fail);

	$HTTP_POST_VARS[name] = fixfqdn($HTTP_POST_VARS[name]);
	if (!strlen($HTTP_POST_VARS[name])) return notice("Name field empty.".$fail);
	if (strlen($HTTP_POST_VARS[name]) > 255) return notice("Name field too long.".$fail);

	return (0);
}
/*--- check_ptr_formdata() ----------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	PTRUPDATE
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function ptrupdate()
{
	global $HTTP_POST_VARS;

	if (check_ptr_formdata("updated") == -1)
		return;

	$q  = "UPDATE ptr SET ";
	$q .= "name='" . esc($HTTP_POST_VARS["name"]) . "'";
	$q .= " WHERE id=" . (int)($HTTP_POST_VARS["ptr"]);

	mysql_query($q) or fatalSQL("Error updating PTR record.");
}
/*--- ptrupdate() -------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	PTR_DELETE
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function ptr_delete()
{
	global $HTTP_POST_VARS;

	$q  = "DELETE FROM ptr WHERE id=" . (int)$HTTP_POST_VARS[ptr];

	mysql_query($q) or fatalSQL("Error deleting PTR record.");
}
/*--- ptr_delete() ------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	PTRINSERT
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function ptrinsert()
{
	global $HTTP_POST_VARS;

	if (check_ptr_formdata("created") == -1)
		return;

	$q  = "INSERT INTO ptr SET ";
	$q .= "ip=INET_ATON('" . esc($HTTP_POST_VARS["dd"]) . "')";
	$q .= ",name='" . esc($HTTP_POST_VARS["name"]) . "'";

	mysql_query($q) or fatalSQL("Error creating PTR record.");
}
/*--- ptrinsert() -------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	MAIN
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
open_page();

mysql_connect($dbhost, $dbuser, $dbpass) or fatalSQL("Unable to connect to database.");
mysql_select_db($dbname) or fatalSQL("Unable to select database.");

$query = trim($query);

switch ($action)
{
	case "soaupdate":
		soaupdate();
		break;

	case "rrupdate":
		if ($rraction == "Delete")
			rr_delete();
		else if ($rraction == "Update")
			rrupdate();
		break;

	case "rrinsert":
		rrinsert();
		break;

	case "ptrupdate":
		if ($ptraction == "Delete")
			ptr_delete();
		else if ($ptraction == "Update")
			ptrupdate();
		break;

	case "ptrinsert":
		ptrinsert();
		break;

	default:
		if ($action)
			notice("Unknown action: &quot;$action&quot;");
		break;
}

show_form($query);
close_page();

/*-----------------------------------------------------------------------------------------------*/

/* vi:set ts=3: */ ?>
